<?php
header('Content-type: text/plain');

require_once('frontstart.inc.php');   # defines: $options[], $context

class Application {
    function handle_request ($options) {
        try {
            global $debug_sql;
            $context = App::context($options);
            $authdb   = $context->authdb();
            $neat    = (isset($_REQUEST['neat']) && $_REQUEST['neat']) ? 1 : 0;
            $type    = isset($_REQUEST['type']) ? $_REQUEST['type'] : '';
            if ($type == '') throw new \Exception('ERROR: type parameter must be set');
            $quoted_type = $authdb->_quote($type);

            if (isset($options['test_mode']) && $options['test_mode']) {
                if (isset($_REQUEST['debug_sql'])) {
                    if (! preg_match('/^-?[0-9]+$/', $_REQUEST['debug_sql'])) throw new \Exception('ERROR: the "debug_sql" parameter contains illegal characters (must be an integer)');
                    $debug_sql = $_REQUEST['debug_sql'];
                }
            }

            $op      = (isset($_REQUEST['op']) && $_REQUEST['op']) ? $_REQUEST['op'] : 'get';
            $results = Array();

            if ($op != 'get') {
                $tree_node_id        = (isset($_REQUEST['tree_node_id'])        && $_REQUEST['tree_node_id'])        ? $_REQUEST['tree_node_id']        : '';
                if ($tree_node_id && !preg_match('/^[0-9]+$/',$tree_node_id)) throw new \Exception("ERROR: tree_node_id is not numeric");

                $parent_id           = (isset($_REQUEST['parent_id'])           && $_REQUEST['parent_id'])           ? $_REQUEST['parent_id']           : '';
                if ($parent_id == 'src') $parent_id = 'NULL';
                elseif ($parent_id && !preg_match('/^[0-9]+$/',$parent_id)) throw new \Exception("ERROR: parent_id is not numeric");

                $label               = (isset($_REQUEST['label'])               && $_REQUEST['label'])               ? $_REQUEST['label']               : '';
                $action              = (isset($_REQUEST['action'])              && $_REQUEST['action'])              ? $_REQUEST['action']              : '';

                $previous_sibling_id = (isset($_REQUEST['previous_sibling_id']) && $_REQUEST['previous_sibling_id']) ? $_REQUEST['previous_sibling_id'] : '';
                if ($previous_sibling_id && !preg_match('/^[0-9]+$/',$previous_sibling_id)) throw new \Exception("ERROR: previous_sibling_id is not numeric");

                $next_sibling_id     = (isset($_REQUEST['next_sibling_id'])     && $_REQUEST['next_sibling_id'])     ? $_REQUEST['next_sibling_id']     : '';
                if ($next_sibling_id && !preg_match('/^[0-9]+$/',$next_sibling_id)) throw new \Exception("ERROR: next_sibling_id is not numeric");

                $drop_position       = (isset($_REQUEST['drop_position'])       && $_REQUEST['drop_position'])       ? $_REQUEST['drop_position']       : '';   # before, after, append, prepend

                $results['success']  = true;   # assume we will be successful
            }

            if ($op == 'new') {
                if (!$parent_id)    throw new \Exception("ERROR: parent_id must be supplied for a 'new' operation");
                if (!$label)        throw new \Exception("ERROR: label must be supplied for a 'new' operation");
                $quoted_label    = $authdb->_quote($label);
                $leaf_ind        = ($action != '') ? "'Y'" : "'N'";
                $quoted_action   = ($action != '') ? $authdb->_quote($action) : 'NULL';
                $authdb->execute("insert into app_tree (tree_type, tree_label, action, leaf_ind, parent_tree_id, order_idx) values ($quoted_type, $quoted_label, $quoted_action, $leaf_ind, $parent_id, 9999)");
                $results['tree_id'] = $authdb->last_insert_id("app_tree");
                $this->reorder_tree($authdb, $type, $parent_id);
            }
            elseif ($op == 'delete') {
                #if (!$parent_id)    throw new \Exception("ERROR: parent_id must be supplied for a 'delete' operation");
                if (!$tree_node_id) throw new \Exception("ERROR: tree_node_id must be supplied for a 'delete' operation");
                $authdb->execute("delete from app_tree where tree_id = $tree_node_id");
                #$this->reorder_tree($authdb, $type, $parent_id);   # not really necessary
            }
            elseif ($op == 'move') {
                if (!$parent_id)    throw new \Exception("ERROR: parent_id must be supplied for a 'move' operation");
                if (!$tree_node_id) throw new \Exception("ERROR: tree_node_id must be supplied for a 'move' operation");
                $this->reorder_tree($authdb, $type, $parent_id, $tree_node_id, $previous_sibling_id, $next_sibling_id, $drop_position);
            }
            else {
                $matches = Array();
                $get_root_node = 0;
    
                if (isset($_REQUEST['mode'])) {
                    $mode = $_REQUEST['mode'];  # e.g. "getTree"
                    if ($mode == 'getTree') {
                        if (isset($_REQUEST['node']) &&
                            isset($_REQUEST['id'])) {
                            $node = $_REQUEST['node'];
                            $id   = $_REQUEST['id'];
                            #echo "node=[$node] mode=[$mode] id=[$id]\n";
                            if ($node == 'src' && $id == 'src') {
                                $get_root_node = 1;
                            }
                            else {
                                $tree_nodes = $authdb->get_hashes("select tree_id, tree_label, leaf_ind, action from app_tree where tree_type = $quoted_type and parent_tree_id = $node order by order_idx, tree_label");
                                foreach ($tree_nodes as $tree_node) {
                                   $results[] = Array('id'=>$tree_node['tree_id'], 'text'=>$tree_node['tree_label'], 'expanded'=>false,
                                                       'leaf'=>($tree_node['leaf_ind']=='Y'), 'action'=>$tree_node['action']);
                                }
                            }
                        }
                        else {
                            throw new \Exception("ERROR: node and id must both be set (often, these are the same) ('src' is the root node/id)");
                        }
                    }
                    else {
                        throw new \Exception("ERROR: unrecognized mode on tree [$mode] (did you mean 'getTree'?)");
                    }
                }
                else {
                    throw new \Exception("ERROR: a valid mode (e.g. 'getTree') must be supplied with a 'get' operation");
                }
                if ($get_root_node) {
                    $full = (isset($_REQUEST['full']) && $_REQUEST['full']) ? 1 : 0;
                    $tree_nodes = $authdb->get_hashes("select tree_id, tree_label, leaf_ind, action from app_tree where tree_type = $quoted_type and parent_tree_id is null order by order_idx, tree_label");
                    if ($full) {
                        foreach ($tree_nodes as $tree_node) {
                            $results[] = Array('id'=>$tree_node['tree_id'], 'text'=>$tree_node['tree_label'], 'expanded'=>false,
                                               'leaf'=>($tree_node['leaf_ind']=='Y'), 'action'=>$tree_node['action']);
                        }
                        # do more stuff here ...
                    }
                    else {
                        foreach ($tree_nodes as $tree_node) {
                            $results[] = Array('id'=>$tree_node['tree_id'], 'text'=>$tree_node['tree_label'], 'expanded'=>false,
                                               'leaf'=>($tree_node['leaf_ind']=='Y'), 'action'=>$tree_node['action']);
                        }
                    }
                }
            }

            $json = json_encode($results);
            if ($neat) {
                $json = preg_replace('/\\},\\{/', "},\n {", $json);
                $json = preg_replace('/\\],\\[/', "],\n [", $json);
                $json = preg_replace('/\\[\\{/', "[\n {", $json);
                $json = preg_replace('/\\}\\]/', "}\n ]", $json);
            }
            echo $json . "\n";
        }
        catch (Exception $e) {
            $this->return_json_error($e->getMessage());
        }
        return(null);
    }

    function reorder_tree ($authdb, $type, $parent_id, $tree_node_id = 0, $previous_sibling_id = 0, $next_sibling_id = 0, $drop_position = '') {
        $quoted_type     = $authdb->_quote($type);
        $parent_id_where_clause = ($parent_id == 'NULL') ? 'is NULL' : "= $parent_id";
        $siblings = $authdb->get_hashes("select tree_id, order_idx from app_tree where tree_type = $quoted_type and parent_tree_id $parent_id_where_clause order by order_idx, tree_label");
        $order_idx = 1;
        $node_done = $tree_node_id ? 0 : 1;
        $reorder_reqd = ($previous_sibling_id || $next_sibling_id || $drop_position) ? 1 : 0;
        if (!$node_done && $drop_position == 'prepend') {
            $nrows = $authdb->execute("update app_tree set parent_tree_id = $parent_id, order_idx = $order_idx where tree_id = $tree_node_id");
            $order_idx++;
            $node_done = 1;
        }
        foreach ($siblings as $sibling) {
            if (!$node_done && $next_sibling_id && $sibling['tree_id'] == $next_sibling_id) {
                $nrows = $authdb->execute("update app_tree set parent_tree_id = $parent_id, order_idx = $order_idx where tree_id = $tree_node_id");
                $order_idx++;
                $node_done = 1;
            }
            if ($tree_node_id && $sibling['tree_id'] == $tree_node_id) {
                if (!$node_done && !$reorder_reqd) {
                    $nrows = $authdb->execute("update app_tree set parent_tree_id = $parent_id, order_idx = $order_idx where tree_id = $tree_node_id");
                    $order_idx++;
                    $node_done = 1;
                }
            }
            else {
                if ($sibling['order_idx'] != $order_idx) {
                    $nrows = $authdb->execute("update app_tree set order_idx = $order_idx where tree_id = $sibling[tree_id]");
                }
                $order_idx++;
            }
            if (!$node_done && $previous_sibling_id && $sibling['tree_id'] == $previous_sibling_id) {
                $nrows = $authdb->execute("update app_tree set parent_tree_id = $parent_id, order_idx = $order_idx where tree_id = $tree_node_id");
                $order_idx++;
                $node_done = 1;
            }
        }
        if (!$node_done) {
            $nrows = $authdb->execute("update app_tree set parent_tree_id = $parent_id, order_idx = $order_idx where tree_id = $tree_node_id");
            $order_idx++;
            $node_done = 1;
        }
    }

    function return_json_error ($message) {
        if (preg_match("/\n/",$message)) {
            $message = preg_replace("/ *\n */"," ",$message);
        }
        if (preg_match("/\\'/",$message)) {
            $message = preg_replace("/\\'/","\\'",$message);
        }
        echo "{\n";
        echo "    'success': false,\n";
        echo "    'msg': '$message'\n";
        echo "}\n";
    }
}

$application = new Application();
$application->handle_request($options);

